// File:			R5.cpp
// Authors:			Richard Burris, Joshua Douty
// Purpose:			R5 work file
// Version:        	1.0
// Date:			11/23/2011

#include <dos.h>
#include "mpx_supt.h"
#include "r5.h"

#define COM1_INT_ID     0x000C
#define COM1_BASE       0x3F8
#define COM1_INT_EN     0x3F9
#define COM1_BRD_LSB    0x3F8
#define COM1_BRD_MSB    0x3F9
#define COM1_INT_ID_REG 0x3FA
#define COM1_LC         0x3FB
#define COM1_MC         0x3FC
#define COM1_LS         0x3FD
#define COM1_MS         0x3FE
#define PIC_MASK        0x21
#define PIC_CMD         0x20
#define EOI             0x20

//com_read errors
#define NOT_OPEN          -301
#define INVALID_ADD       -302
#define INVALID_COUNT_ADD -303
#define BUSY              -304

//com_open errors
#define INVALID_FLAG -101
#define INVALID_BAUD -102
#define PORT_OPEN    -103

#define PORT_CLOSED  -201

//com_write errors
#define PORT_NOT_OPEN   -401
#define INVALID_BUF_ADD -402
#define INVALID_COUNT   -403
#define DEVICE_BUSY     -404

#define IO 0
#define IOCOMP 1


int ioflag = IO;

void exit();
int com_close();
int com_read(char* buf_p, int *count_p);
int com_open(int *eflag_p, int baud_rate);
int com_write(char* buf_p, int *count_p);
void interrupt inter(...);
void read_port();
void setDCB(int *eflag_p);
void write_port();

void push_char(char c);
char pull_char(void);

char buf[80];

void interrupt (*oldfunc) (...);

DCB_Pointer DevConBlock = (DCB*)(sys_alloc_mem(sizeof(Dev_Con_Block)));

void exit() {	
		int temp = 52;
		sys_req(WRITE, TERMINAL, "Please type yes to exit!\nOr press enter to continue", &temp);
	
		//Prints out the Console and waits for input
		char* str0 = "\n\n> \0";
		int size = strlen(str0);
		strcpy (buf, str0);
		sys_req(WRITE, TERMINAL, buf, &size);
		size = 80;
		sys_req(READ, TERMINAL, buf, &size);
	
		if (strncmp(buf, "yes", 3) == 0) {
			int temp = 14;
			sys_req(WRITE, TERMINAL, "Exiting Now...", &temp);
			sys_exit();
		}
}

int com_close() {
	if(DevConBlock->ocFlag == CLOSED) {
		return PORT_CLOSED;
	}
	
	DevConBlock->ocFlag = CLOSED;
	
	int mask;
	disable();
	mask = inportb(PIC_MASK);
	mask = mask & ~0x20;
	//mask = 0x20;
	outportb(PIC_MASK, mask);
	enable();
	
	mask = 0x00;
	//Modem status register
	outportb(COM1_MC, mask);
	//Interrupt enable register
	outportb(COM1_INT_EN, mask);
	
	setvect(COM1_INT_ID, oldfunc);
	
	return 0;
}

int com_read(char* buf_p, int *count_p) {
	if(DevConBlock->ocFlag != OPEN) {
		return NOT_OPEN;
	}
	if(buf_p == NULL) {
		return INVALID_ADD;
	}
	if(count_p == NULL) {
		return INVALID_COUNT_ADD;
	}
	if(DevConBlock->sCode != IDLE) {
		return BUSY;
	}
	
	DevConBlock->in_buff = buf_p;
	DevConBlock->in_count = count_p;
	
	DevConBlock->in_done = 0;
	
	*DevConBlock->eFlag = 0;
	
	disable();
	DevConBlock->sCode = READ;
	
	//Need to debug this stuff
	if(DevConBlock->ring_buffer_count > 0) {
		int i = 0;
		while(1) {
			if(DevConBlock->ring_buffer_count <= 0) {
				break;
			}
		
			if(DevConBlock->in_done >= BUF_SIZE) {
				
				break;
				//DevConBlock->in_buff++;
				
				//DevConBlock->ring_buffer[i] = '\0';
			}
			
			if(pull_char() == '\r') {
					break;
			}
			
			*DevConBlock->in_buff++ = pull_char();
			i++;
		}
	}
	
	enable();
	
	if((DevConBlock->in_done) < (BUF_SIZE)) {
		return 0;
	}
	
	*DevConBlock->in_buff = '\0';
	//DevConBlock->in_buff++;
	
	
	DevConBlock->sCode = IDLE;
	
	*DevConBlock->in_count = DevConBlock->in_done;
	
	return 0;
}

int com_open(int *eflag_p, int baud_rate) {
	if(eflag_p == NULL) {
		return INVALID_FLAG;
	}
	
	if(baud_rate <= 0) {
		return INVALID_BAUD;
	}
	
	if(DevConBlock->ocFlag == OPEN) {
		return PORT_OPEN;
	}
	
	setDCB(eflag_p);
	
	//Old Interrupt Handler 
	oldfunc = getvect(COM1_INT_ID);

	//New Interrupt Handler
	setvect(COM1_INT_ID, &inter);
	
	long baud_rate_div =(115200)/(long) baud_rate;
	
	//In uncharted waters here
	outportb(COM1_LC, 0x80);

	outportb(COM1_BRD_LSB, baud_rate_div&0xFF);
	outportb(COM1_BRD_LSB,(baud_rate_div >> 8) & 0xFF);
	
	outportb(COM1_LC, 0x03);
	
	//Look here
	//*****************************************************************************
	//int mask = 0x80;
	int mask;
	disable();
	mask = inportb(PIC_MASK);
	mask = mask & ~0x80;
	mask = 0x80;
	outportb(PIC_MASK, mask);
	enable();
	
	outportb(COM1_MC, 0x08);
	outportb(COM1_INT_EN, 0x01);
	
	return 0;
}
 
int com_write(char* buf_p, int *count_p) {
	if(DevConBlock->ocFlag == CLOSED) {
		return PORT_NOT_OPEN;
	}
	if(DevConBlock->sCode != IDLE) {
		return DEVICE_BUSY;
	}
	if(buf_p == NULL) {
		return INVALID_BUF_ADD;
	}
	if(count_p == NULL) {
		return INVALID_COUNT;
	}
	

	DevConBlock->out_buff = buf_p;
	DevConBlock->out_count = count_p;
	DevConBlock->out_done = 0;
	
	DevConBlock->sCode = WRITE;
	
	int foo = 0;
	*DevConBlock->eFlag = foo;
	
	outportb(COM1_BASE, *DevConBlock->out_buff);
	DevConBlock->out_buff++;
	DevConBlock->out_done++;
	
	int mask = inportb(COM1_INT_EN);
	mask = mask | 0x02;
	outportb(COM1_INT_EN, mask);
	
	return 0;
}

void read_port() {
	char inputchar = inportb(COM1_BASE);
	
	if(DevConBlock->sCode != READ) {
		if(DevConBlock->ring_buffer_count >= BUF_SIZE) {
			return;
		}
		
		push_char(inputchar);

		return;
	}
	
	if(DevConBlock->sCode == READ) {
	
		*DevConBlock->in_buff++ = inputchar;
		//DevConBlock->in_buff++;
		
		*DevConBlock->in_count++;
		
		if((inputchar == '\r') || (*DevConBlock->in_count >= BUF_SIZE)) {
			
			*DevConBlock->in_buff = '\0';
			//DevConBlock->in_buff++;
			
			
			DevConBlock->sCode = IDLE;
			
			*DevConBlock->eFlag = 1;
			
			*DevConBlock->in_count = DevConBlock->in_done;
		}
	}
}

void interrupt inter(...) {

	if(DevConBlock->ocFlag == CLOSED) {
		outportb(PIC_CMD, EOI);
		return;
	}
	
	int cause = inportb(COM1_INT_ID_REG);
	cause = cause & 0x07;
	
	if(cause == 2) {
		write_port();
	} else if(cause == 4) {
		read_port();
	}
	
	outportb(PIC_CMD, EOI);
	return;
}

void setDCB(int *eflag_p) {
	DevConBlock->ocFlag = OPEN;
	DevConBlock->eFlag = eflag_p;
	*DevConBlock->eFlag = 0;
	DevConBlock->sCode = IDLE;
	
	DevConBlock->in_buff = (char*)sys_alloc_mem(sizeof(DevConBlock->in_buff));
	*DevConBlock->in_count = 0;
	DevConBlock->in_done = 0;
	
	DevConBlock->out_buff = (char*)sys_alloc_mem(sizeof(DevConBlock->out_buff));
	*DevConBlock->out_buff = 0;
	DevConBlock->out_done = 0;
	
	//(char*)DevConBlock->ring_buffer
	int i;
	//Setting Ring buffer
	for (i = 0; i < BUF_SIZE; i++) DevConBlock->ring_buffer[i] = NULL;
	DevConBlock->ring_buffer_in = 0;
	DevConBlock->ring_buffer_out = 0;
	DevConBlock->ring_buffer_count = 0;
}

void write_port() {
	if(DevConBlock->sCode != WRITE) {
		return;
	}
	
	int out = *DevConBlock->out_count;
	if((DevConBlock->out_done) < (out)) {
		char* outbuff = DevConBlock->out_buff;
		char obuff = *outbuff;
		int temp = 9;
		//outportb(COM1_BASE, *DevConBlock->out_buff);
		outportb(COM1_BASE, obuff);
		DevConBlock->out_buff++;
		DevConBlock->out_done++;
	} else { 
		
		DevConBlock->sCode = IDLE;
		
		int foo = 1;
		*DevConBlock->eFlag = foo;
		int mask = inportb(COM1_INT_EN);
		mask = mask & ~0x02;
		outportb(COM1_INT_EN, mask);
	}
	
	return;
}

void push_char(char c)
{
    // increase write_pointer, check if at end of array
    if (++DevConBlock->ring_buffer_in > BUF_SIZE) { 
		DevConBlock->ring_buffer_in = 0;
	}
	
    DevConBlock->ring_buffer[++DevConBlock->ring_buffer_in] = c;    
    DevConBlock->ring_buffer_count++;
}

char pull_char(void) 
{  
	if (++DevConBlock->ring_buffer_out >= BUF_SIZE) {
		DevConBlock->ring_buffer_out = 0;
	}
 
	char out = DevConBlock->ring_buffer[DevConBlock->ring_buffer_out];
  //printf("\nPopped char %c", buffer[read_pointer]);
 
  // enter space on place of read char so we can see it is removed
  DevConBlock->ring_buffer[DevConBlock->ring_buffer_out] = NULL; 
  DevConBlock->ring_buffer_count--;  
  
  return out;
}